package rescuecore2.standard.kernel.comms;

import java.util.Map;

import rescuecore2.config.Config;
import rescuecore2.worldmodel.Entity;
import rescuecore2.worldmodel.EntityID;
import rescuecore2.misc.collections.LazyMap;
import rescuecore2.log.Logger;

import rescuecore2.standard.messages.AKSpeak;
import rescuecore2.standard.entities.StandardWorldModel;
import rescuecore2.standard.entities.StandardEntity;
import rescuecore2.standard.entities.StandardEntityURN;
import rescuecore2.standard.entities.Human;

/**
   A voice channel.
*/
public class VoiceChannel extends AbstractChannel {
    // Config option suffixes
    private static final String RANGE_SUFFIX = ".range";
    private static final String MESSAGE_SIZE_SUFFIX = ".messages.size";
    private static final String MESSAGE_MAX_SUFFIX = ".messages.max";

    private int range;
    private int maxSize;
    private int maxMessages;
    private Map<EntityID, Integer> uttered;
    private StandardWorldModel world;

    /**
       Create a VoiceChannel.
       @param config The configuration to read.
       @param channelID The id of this channel.
       @param world The world model.
    */
    public VoiceChannel(Config config, int channelID, StandardWorldModel world) {
        super(channelID);
        this.world = world;
        range = config.getIntValue(ChannelCommunicationModel.PREFIX + channelID + RANGE_SUFFIX);
        maxSize = config.getIntValue(ChannelCommunicationModel.PREFIX + channelID + MESSAGE_SIZE_SUFFIX);
        maxMessages = config.getIntValue(ChannelCommunicationModel.PREFIX + channelID + MESSAGE_MAX_SUFFIX);
        uttered = new LazyMap<EntityID, Integer>() {
            @Override
            public Integer createValue() {
                return 0;
            }
        };
    }

    @Override
    public void timestep() {
        super.timestep();
        uttered.clear();
    }

    @Override
    protected void pushImpl(AKSpeak speak) throws InvalidMessageException {
        EntityID agentID = speak.getAgentID();
        Entity e = world.getEntity(agentID);
        if (!(e instanceof Human)) {
            throw new InvalidMessageException("Agent " + agentID + " is not a human: " + (e == null ? "null" : e.getClass().getName()));
        }
        byte[] data = speak.getContent();
        int count = uttered.get(agentID);
        if (count >= maxMessages) {
            throw new InvalidMessageException("Agent " + agentID + " has uttered too many voice messages on " + this);
        }
        if (data.length > maxSize) {
            throw new InvalidMessageException("Agent " + agentID + " tried to send an oversize voice message: " + data.length + " bytes but the limit is " + maxSize);
        }
        uttered.put(agentID, count + 1);
        // Find out who can hear it
        StandardEntity sender = world.getEntity(agentID);
        for (StandardEntity target : world.getEntitiesOfType(StandardEntityURN.FIRE_BRIGADE, StandardEntityURN.AMBULANCE_TEAM, StandardEntityURN.POLICE_FORCE, StandardEntityURN.CIVILIAN)) {
            if (world.getDistance(sender, target) <= range) {
                Logger.debug(target + " can hear voice message from " + sender);
                addMessageForAgent(target, speak);
            }
        }
    }

    @Override
    public String toString() {
        return "Voice channel " + channelID + " (range = " + range + ", max " + maxMessages + " messages of size " + maxSize + ")";
    }
}
